import networkUtils from "./networkUtils";
import User from "./user";
import { Entrust, Position } from "./exchangeUtils";
import { calcMostOpenableBtc, calcUsdBond } from "./calcUtils";

var app_context = {
  login_user: new User({
    username: "游客",
    isTourist: true,
    usdTradingAssert: 1703.16,
  }),
  btc_info: {
    indexPrice: 0.0,
    timestamp: 0,
  },
};
var wsClient;
var timerPingTimeout;
var subscribers = {
  onTickerPush: new Map(),
};
var channels = {
  BTC_INFO: {
    sub(callback) {
      subscribers.onTickerPush.set(callback, callback);
      return callback;
    },
    unsub(key) {
      subscribers.onTickerPush.delete(key);
    },
  },
};

function onMessage(event) {
  let body = JSON.parse(event.data);
  let data = body.data;
  let channel = body.channel;
  if (channel === "pong") {
    console.log("Channel pong arrived in " + (Date.now() - data) + "ms");
  } else if (channel === "push.ticker") {
    let newData = data;
    let oldData = app_context.btc_info;
    subscribers.onTickerPush.forEach((f) => f(newData, oldData));
    updateBtcInfo(newData);
    handelEntrust();
  } else {
    console.log('Unhandle channel "' + data.channel + '"');
  }
}

function connectInit() {
  wsClient = null;
  clearTimeout(timerPingTimeout);
}

function connectClose() {
  app_context.btc_info.timestamp = 0;
  app_context.btc_info.indexPrice = 0;
}

function afterConnect() {
  timerPing();
  wsClient.send(
    JSON.stringify({
      method: "sub.ticker",
      param: {
        symbol: "BTC_USDT",
      },
    })
  );
}

function connectToServer() {
  connectInit();
  let period = Date.now();
  console.log("正在连接服务器...");
  let client = networkUtils.newWSClient("wss://contract.mexc.com/ws");
  client.onmessage = onMessage;
  client.onclose = (event) => {
    console.log("与服务器连接丢失, 3秒后尝试重新连接", event);
    setTimeout(connectToServer, 3000);
    connectClose();
  };
  client.onopen = (event) => {
    wsClient = client;
    console.log("连接服务器成功, 耗时" + (Date.now() - period) + "ms", event);
    afterConnect();
  };
}

function closePosition({ btcCount, finalPrice, isShort }) {
  let user = app_context.login_user;
  let position = user.getPosition(isShort);
  if (position != null) {
    let profit = position.calcProfit(finalPrice); // 计算全平后带来的总收益

    // 全平
    if (Math.floor(btcCount) >= Math.floor(position.btcCount)) {
      user.addUsdTradingAssert(profit + position.usdBond); // 归还保证金和收益
      user.removePosition(position.id);
      return;
    }
    profit *= btcCount / position.btcCount; // 计算部分收益
    user.updatePosition(
      new Position({
        id: position.id,
        avgFinalPrice: position.avgFinalPrice,
        btcCount: position.btcCount - btcCount,
        usdBond: position.usdBond + profit, // 将收益添加到保证金
        isShort: position.isShort,
        finalPrice: finalPrice,
      })
    );
  }
}

function openPosition({ usdBond, btcCount, finalPrice, isShort }) {
  let position = app_context.login_user.getPosition(isShort);
  if (position != null) {
    app_context.login_user.updatePosition(
      new Position({
        id: position.id,
        usdBond: position.usdBond + usdBond,
        btcCount: position.btcCount + btcCount,
        isShort,
        avgFinalPrice: (position.avgFinalPrice + finalPrice) / 2,
      })
    );
    return;
  }
  app_context.login_user.addPosition(
    new Position({
      id: Date.now(),
      usdBond,
      btcCount,
      isShort,
      avgFinalPrice: finalPrice,
    })
  );
}

function completeEntrust(entrust, finalPrice) {
  let user = app_context.login_user;
  user.completeEntrust(entrust.id, finalPrice);
}

// function cancelEntrust(id) {
//   let user = app_context.login_user;
//   user.cancelEntrust(id);
// }

/**
 * 处理登录用户的所有委托
 */
function handelEntrust() {
  let indexPrice = getValidIndexPrice();
  let user = app_context.login_user;
  if (indexPrice == null) {
    return;
  }
  for (const e of Object.values(user.entrusts).filter(
    (e) => !e.isCompleted & !e.isCancelled
  )) {
    let myOffer = e.indexPrice;
    if (e.isOpen) {
      if (!e.isShort && myOffer >= indexPrice) {
        openPosition({
          usdBond: e.usdBond,
          btcCount: e.btcCount,
          finalPrice: indexPrice,
          isShort: e.isShort,
        });
        completeEntrust(e, indexPrice);
      } else if (e.isShort && myOffer <= indexPrice) {
        openPosition({
          usdBond: e.usdBond,
          btcCount: e.btcCount,
          finalPrice: indexPrice,
          isShort: e.isShort,
        });
        completeEntrust(e, indexPrice);
      }
    } else if (!e.isOpen) {
      if (e.isShort && myOffer >= indexPrice) {
        closePosition({
          btcCount: e.btcCount,
          finalPrice: indexPrice,
          isShort: e.isShort,
        });
        completeEntrust(e, indexPrice);
      } else if (!e.isShort && myOffer <= indexPrice) {
        closePosition({
          btcCount: e.btcCount,
          finalPrice: indexPrice,
          isShort: e.isShort,
        });
        completeEntrust(e, indexPrice);
      }
    }
  }
}

function getValidIndexPrice() {
  let btc_info = app_context.btc_info;
  let delay = Date.now() - btc_info.timestamp;
  if (delay >= 5000) {
    console.log("invalided price, delay " + delay + "ms");
    return null;
  }
  return btc_info.indexPrice;
}

/**
 * 计算当前用户最大可开的虚拟货币数量
 * @param lever 杠杆倍数
 * @param indexPrice 购买价格
 */
function getMostOpenableBtc(lever, indexPrice, isShort = false) {
  if (isShort) {
    let p = getValidIndexPrice();
    if (p == null) {
      return 0;
    }
    if (p > indexPrice) {
      indexPrice = p;
    }
  }
  return calcMostOpenableBtc(
    app_context.login_user.usdTradingAssert,
    lever,
    indexPrice
  );
}

function getMostCloseAbleBtc(isShort) {
  for (const o of app_context.login_user.positions) {
    if (o.isShort === isShort) {
      return o.btcCount;
    }
  }
  return 0;
}

function timerPing() {
  if (wsClient == null) {
    console.log("timer ping stoped");
    return;
  }
  wsClient.send('{ method: "ping" }');
  timerPingTimeout = setTimeout(timerPing, 12000);
}

function updateBtcInfo(info) {
  let betInfo = app_context.btc_info;
  betInfo.indexPrice = info.indexPrice;
  betInfo.timestamp = info.timestamp;
}

function getBtcInfo() {
  return JSON.parse(JSON.stringify(app_context.btc_info));
}

// function getUserUsdTradingAssert() {
//   return app_context.login_user.usdTradingAssert;
// }

function subUserBtcAssert(num) {
  let user = app_context.login_user;
  if (user.btcAssert < num) {
    throw new Error("sub assert error, target num greater than assert.");
  }
  user.subBtcAssert(num);
}

function subUserTradingUsdAssert(num) {
  let user = app_context.login_user;
  let before = user.usdTradingAssert;
  if (user.usdTradingAssert < num) {
    throw new Error(
      `sub assert error, target num ${num} greater than assert ${user.usdTradingAssert}.`
    );
  }
  user.subUsdTradingAssert(num);
  return before;
}

function subUserUsdAssert(num) {
  let user = app_context.login_user;
  if (user.usdAssert < num) {
    throw new Error("sub assert error, target num greater than assert.");
  }
  user.subUsdAssert(num);
}

function getUser() {
  return app_context.login_user;
}

/**
 * 将委托对象添加进登录用户.
 */
function addEntrust(entrust) {
  let user = app_context.login_user;
  if (entrust.isOpen) {
    subUserTradingUsdAssert(entrust.usdBond);
  } else {
    if (entrust.isShort) {
      console.log("TODO: handle close short position enturst");
    } else {
      console.log("TODO: handle close long position enturst");
    }
  }
  user.addEntrust(entrust);
}

/**
 * 为当前用户创建一个委托, 并校验是委托否合法.
 */
function openEntrust({
  isShort,
  type,
  indexPrice,
  btcCount,
  lever,
  isCurrentPrice = false,
  isOpen = true,
  successCallback = () => {},
  errCallback = () => {},
}) {
  indexPrice = isCurrentPrice ? getValidIndexPrice() : indexPrice;
  if (indexPrice <= 0) {
    errCallback("请输入价格");
    return;
  }
  if (isOpen && lever <= 0) {
    errCallback("网络错误");
    return;
  }

  let mostOpenAbleBtc = getMostOpenableBtc(lever, indexPrice, isShort);
  let mostCloseAbleBtc = getMostCloseAbleBtc(isShort);
  if (isOpen && (btcCount > mostOpenAbleBtc || mostOpenAbleBtc <= 0)) {
    errCallback("可开2B数量不足");
    return;
  } else if (
    !isOpen &&
    (btcCount > mostCloseAbleBtc || mostCloseAbleBtc <= 0)
  ) {
    errCallback("可平2B数量不足");
  }

  if (btcCount <= 0) {
    errCallback("请输入数量");
    return;
  }

  try {
    addEntrust(
      new Entrust({
        id: Date.now(),
        indexPrice,
        lever,
        type,
        usdBond: calcUsdBond({ indexPrice, lever, btcCount }),
        isShort,
        btcCount,
        isOpen,
      })
    );
  } catch (err) {
    errCallback(err);
    return;
  }
  successCallback("委托成功");
  return handelEntrust();
}

export { openEntrust };

export { subUserBtcAssert, subUserUsdAssert, subUserTradingUsdAssert };

export { getUser, getMostOpenableBtc };

export { channels, getBtcInfo, getValidIndexPrice, calcMostOpenableBtc };

export default {
  app_context,
  channels,
  start() {
    connectToServer();
    history.pushState(null, null, document.URL);
    window.addEventListener("popstate", function () {
      history.pushState(null, null, document.URL);
    });
  },
};
